package com.gitee.deeround.jdbchelper.resultSetExtractors;

import com.gitee.deeround.jdbchelper.exception.DbException;
import com.gitee.deeround.jdbchelper.util.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class ListObjectResultSetExtractor<T> extends BaseResultSetExtractor implements ResultSetExtractor<List<T>> {

    private Class<T> elementType;

    public ListObjectResultSetExtractor(Class<T> elementType) {
        this.elementType = elementType;
    }

    @Override
    public List<T> extractData(ResultSet rs) throws SQLException, DataAccessException {
        List<T> list = new ArrayList<>();
        ResultSetMetaData md = rs.getMetaData();
        int columnCount = md.getColumnCount();

        try {
            List<Field> allFiled = getAllFiled(elementType.newInstance());
            Map<String, Object> map = new HashMap<>();
            while (rs.next()) {
                T t = (T) elementType.newInstance();
                for (int j = 0; j < columnCount; j++) {
                    String columnName = resolveColumnName(md.getColumnLabel(j + 1));
                    Object columnValue = getColumnValue(rs, j + 1, columnName, resolveDataType(md.getColumnType((j + 1))));
                    map.put(columnName.toLowerCase().replaceAll("_", ""), columnValue);
                }

                for (Field field : allFiled) {
                    String key = field.getName().toLowerCase();
                    if (map.containsKey(key)) {
                        setProperty(t, field, map.get(key));
                    }
                }

                list.add(t);
            }
        } catch (Throwable e) {
            throw new DbException("赋值异常：" + e.getMessage());
        }

        return list;
    }

    public void setProperty(Object obj, Field field, Object value) throws IllegalAccessException {
        try {
            PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(obj.getClass(), field.getName());
            Method setMethod = pd.getWriteMethod();
            if (setMethod != null) {
                if (value == null) {
                    return;
                }

                Class<?> type = field.getType();
                if (type.isAssignableFrom(value.getClass())) {
                    setMethod.invoke(obj, value);
                } else {

                    String str = value.toString();
                    if (StringUtils.isEmpty(str)) {
                        return;
                    }

                    //日期转换
                    if (type.isAssignableFrom(Date.class) && LocalDateTime.class.isAssignableFrom(value.getClass())) {
                        Date date = Date.from(((LocalDateTime) value).atZone(ZoneId.systemDefault()).toInstant());
                        setMethod.invoke(obj, date);
                    } else if (type.isAssignableFrom(LocalDateTime.class) && Date.class.isAssignableFrom(value.getClass())) {
                        LocalDateTime date = ((Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                        setMethod.invoke(obj, date);
                    } else if (type.isAssignableFrom(String.class)) {
                        setMethod.invoke(obj, value);
                    } else if (type.isAssignableFrom(BigDecimal.class)) {
                        setMethod.invoke(obj, new BigDecimal(str));
                    } else if (type.isAssignableFrom(Integer.class)
                            || type.isAssignableFrom(int.class)) {
                        setMethod.invoke(obj, Integer.parseInt(str));
                    } else if (type.isAssignableFrom(Double.class)
                            || type.isAssignableFrom(double.class)) {
                        setMethod.invoke(obj, Double.parseDouble(str));
                    } else if (type.isAssignableFrom(Boolean.class)
                            || type.isAssignableFrom(boolean.class)) {
                        setMethod.invoke(obj, "1".equals(str) || "true".equalsIgnoreCase(str) || "Y".equalsIgnoreCase(str));
                    } else if (type.isAssignableFrom(Date.class)) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        setMethod.invoke(obj, dateFormat.parse(str));
                    } else if (type.isAssignableFrom(LocalDateTime.class)) {
                        setMethod.invoke(obj, LocalDateTime.parse(str, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    }
                }
            }
        } catch (Throwable e) {
            throw new DbException("给字段" + field.getName() + "赋值异常：" + e.getMessage());
        }
    }

    private static List<Field> getAllFiled(Object object) {
        Class clazz = object.getClass();
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        return fields;
    }
}
